﻿using System;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using CoreEx.Common.Cache;
using CoreEx.DynamicInvoker;
using LinFu.AOP.Interfaces;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;

namespace CoreEx.ServiceModel.Implementation
{
    /// <summary>
    /// Creates a <see cref="ServiceContractClientInterceptor{TChannel}"/> that  
    /// forwards calls made to a service contract to an <see cref="IChannel"/> instance. 
    /// </summary>
    [Implements(typeof(IServiceContractInterceptorFactory))]
    public class ServiceContractInterceptorFactory : IServiceContractInterceptorFactory, IInitialize
    {
        private ILazyCache<Type, ConstructorInfo> _constructors;

        /// <summary>
        /// Creates a new <see cref="ChannelFactory{TChannel}"/> instance.
        /// </summary>
        /// <param name="channelFactory">The <see cref="ServiceContractClientInterceptor{TChannel}"/> instance
        /// used by the interceptor to create new channels.</param>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public IInterceptor CreateServiceContractInterceptor(ChannelFactory channelFactory, Type serviceType)
        {
            ConstructorInfo constructorInfo = _constructors.GetValue(serviceType, GetConstructorInfo);
            var interceptor = (IInterceptor)constructorInfo.DynamicInvoke(channelFactory);
            return interceptor;
        }

        private static ConstructorInfo GetConstructorInfo(Type serviceType)
        {
            var interceptorType = typeof(ServiceContractClientInterceptor<>).MakeGenericType(serviceType);
            return interceptorType.GetConstructor(new[] { typeof(ChannelFactory) });
        }

        /// <summary>
        /// Initializes this service instance.
        /// </summary>        
        public void Initialize(IServiceContainer source)
        {
            _constructors = source.GetService<ILazyCache<Type, ConstructorInfo>>
                ("ServiceContractInterceptorConstructors");
        }
    }
}